#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

/*
	图像像素读写操作:
		常用类型:
			1、uchar类型
			2、Vec系列类型：
				Vec+数字+字母	相当于	C++STL vector容器
					数字：Vec的长度
					字母：Vec的类型
						b——uchar；s——short；w——ushort；i——int；f——float；d——double；
					
					 @name Shorter aliases for the most popular specializations of Vec<T,n>
					 @{
							typedef Vec<uchar, 2> Vec2b;
							typedef Vec<uchar, 3> Vec3b;
							typedef Vec<uchar, 4> Vec4b;
							
							typedef Vec<short, 2> Vec2s;
							typedef Vec<short, 3> Vec3s;
							typedef Vec<short, 4> Vec4s;
							
							typedef Vec<ushort, 2> Vec2w;
							typedef Vec<ushort, 3> Vec3w;
							typedef Vec<ushort, 4> Vec4w;
							
							typedef Vec<int, 2> Vec2i;
							typedef Vec<int, 3> Vec3i;
							typedef Vec<int, 4> Vec4i;
							typedef Vec<int, 6> Vec6i;
							typedef Vec<int, 8> Vec8i;
							
							typedef Vec<float, 2> Vec2f;
							typedef Vec<float, 3> Vec3f;
							typedef Vec<float, 4> Vec4f;
							typedef Vec<float, 6> Vec6f;
							
							typedef Vec<double, 2> Vec2d;
							typedef Vec<double, 3> Vec3d;
							typedef Vec<double, 4> Vec4d;
							typedef Vec<double, 6> Vec6d;
					}

		图像的像素读写方式：
			1、数组读写方式：

			2.指针读写方式：
					一级指针访问二级数组的方式;

		像素读写的注意点:
			在读取像素点的时候，做一些算数运算，一定要注意颜色范围问题，小心溢出；针对溢出，opencv提供了一个转换防止溢出的函数:
				saturate_cast<要转换的类型>(要转换的数据)
						如果小于0了，就转换成等于0
						如果大于255了，就转换成等于255

*/


//
class Image_Visit_Pixel
{
protected:
	Mat mat;

public:
	Image_Visit_Pixel(const string& filename, int code = IMREAD_UNCHANGED)
		:mat(imread(filename, code))
	{	}

	void Image_Show(const string& type,int pos_x,int pos_y)
	{
		imshow(type, mat);
		moveWindow(type, pos_x, pos_y);
		waitKey();
	}

	//数组方式读写：
	void Visit_By_Array()
	{
		int dims = mat.channels();				//获取图像的通道数；

		for (int r = 0; r < mat.rows; r++)
		{
			for (int c = 0; c < mat.cols; c++)
			{
				if (1 == dims)					//单通道
				{
					//单通道图像像素点获取哦：
					int pixel = mat.at<uchar>(r, c);

					//负片处理：
					mat.at<uchar>(r, c) = 255 - pixel;

				}
				else if (3 == dims)				//三通道
				{
					//三通道图像像素点获取哦:
					Vec3b bgr = mat.at<Vec3b>(r, c);

					//负片处理：
					/*
					mat.at<Vec3b>(r, c)[0] = 255 - bgr[0];
					mat.at<Vec3b>(r, c)[1] = 255 - bgr[1];
					mat.at<Vec3b>(r, c)[2] = 255 - bgr[2];
					*/

					//像素点加成:
					mat.at<Vec3b>(r, c)[0] = saturate_cast<uchar>(3*bgr[0]);
					mat.at<Vec3b>(r, c)[1] = saturate_cast<uchar>(3*bgr[1]);
					mat.at<Vec3b>(r, c)[2] = saturate_cast<uchar>(3*bgr[2]);

				}
			}
		}
	}

	//指针方式读写：
	void Visit_By_Pointer()
	{
		int dims = mat.channels();			//获取图像的通道数;

		for (int r = 0; r < mat.rows; r++)
		{
			//Mat中有个成员函数（ptr），用来获取当前行的指针:
			uchar* currentRow = mat.ptr<uchar>(r);			//获取当前行的指针；

			for (int c = 0; c < mat.cols; c++)
			{
				if (1 == dims)				//单通道
				{
					int pixel = *currentRow;

					//负片处理：
					*currentRow++ = 255 - pixel;				//做一个横向移动;
				}
				else if (3 == dims)
				{
					Vec3b bgr = mat.at<Vec3b>(r, c);

					//负片处理:
					*currentRow++ = 255 - *currentRow;
					*currentRow++ = 255 - *currentRow;
					*currentRow++ = 255 - *currentRow;
				}
			}
		}
	}
};


int main()
{

	unique_ptr<Image_Visit_Pixel> img(new Image_Visit_Pixel("mm.jpg"));
	img->Image_Show("原图",400,400);

	img->Visit_By_Array();
	img->Image_Show("数组读写方式", 600, 600);
	img->Visit_By_Pointer();
	img->Image_Show("指针读取方式", 700, 700);
	//如果两次都是负片处理吗，两次取反的负片处理会把图像还原成原来的样式



	return 0;
}
